<?php

/**
 * Description of httpClient HTTP协议理解使用... 抓接口请使用APIClient
 * 
 * Usage::
 * <code>
class BaiduHomePage extends Lib_HttpClient {
    const Baidu_HOME_PAGE = "http://www.baidu.com/";
    public function __construct() {
        parent::__construct(self::Baidu_HOME_PAGE);
        parent::setFollowLocation(true);
        parent::setTimeOut(1);
        parent::send();
        echo $this->getResponse();
    }
}
$baidu = new BaiduHomePage();
 * </code>
 * 
 * @author smn
 */
abstract class Lib_HttpClient {
    /**
     *  method constants
     */
    const HTTP_METHOD_GET = 1;
    const HTTP_METHOD_POST = 3;
    const HTTP_METHOD_PUT = 4;


    /**
     * version constants
     */
    const HTTP_VERSION_1_0 = 1;
    const HTTP_VERSION_1_1 = 2;


    const HTTP_USER_AGENT = "php client by smn (facebook.com/smn86)";


    protected $_httpProtocol = array("http", "https");
    protected $_methods = array(
        self::HTTP_METHOD_GET => "GET",
        self::HTTP_METHOD_POST => "POST",
        self::HTTP_METHOD_PUT => "PUT"
    );
    protected $_versions = array(
        self::HTTP_VERSION_1_0 => "1.0",
        self::HTTP_VERSION_1_1 => "1.1"
    );
    protected $_context;
    protected $_url;
    protected $_method;
    protected $_headers;
    protected $_follow_location = true;
    protected $_max_redirects = 20;
    protected $_version;
    protected $_timeout = 30;
    protected $_postData = array();
    protected $_response;

    /**
     *
     * @param String $url Url for request
     * @param <type> $param Array of param
     * method => self::HTTP_METHOD_*,
     * content => array("post1" => "value1", "post2" => "value2", "postN" => "valueN"),
     * headers => array("header1 => "value1" , ... ),
     * follow_location => (bool) true/false,
     * max_redirects => number of redirect,
     * protocol_version => self:HTTP_VERSION_*,,
     * timeout => timeout of connection
     */
    public function __construct($url, $param = null) {
        if (is_null($url)) {
            return;
        }

        $key = array_search(parse_url($url, PHP_URL_SCHEME), $this->_httpProtocol);

        if ($this->_httpProtocol[$key] != parse_url($url, PHP_URL_SCHEME)) {
            return;
        }

        $this->setUrl($url); /* set url */
        $this->setParam($param); /* setting params */
    }

    /**
     *
     * @param Array $param params for request , headers, content, method, etc.
     */
    public function setParam($param) {
        /* make param for stream context */

        if ((is_null($param)) || (count($param) == 0)) {
            /* default value */
            $this->setMethod(self::HTTP_METHOD_GET);
            $this->setHeader("User-Agent", self::HTTP_USER_AGENT);
            $this->setHttpVersion(self::HTTP_VERSION_1_1);
            return;
        }



        /* setting method */
        if (array_key_exists("method", $param)) {
            $this->setMethod($param["method"]);
        } else {
            $this->setMethod(self::HTTP_METHOD_GET);
        }




        /* setting content data */

        if (array_key_exists("content", $param)) {
            $this->setPostData($param["content"]);
        }


        /* user-agent */


        $this->setHeader("User-Agent", self::HTTP_USER_AGENT);


        /* setting headers */
        if (array_key_exists("headers", $param)) {
            $this->setHeaders($param["headers"]);
            /* if key User-Agent exists, self::HTTP_USER_AGENT will be overwritten */
        }



        /* setting following and max redirect number */

        if (array_key_exists("follow_location", $param)) {
            $this->setFollowLocation();
        }
        if (array_key_exists("max_redirects", $param)) {
            $this->setMaxRedirect();
        }



        /* setting HTTP version , default value is HTTP/1.1 */
        if (array_key_exists("protocol_version", $param)) {
            $this->setHttpVersion($param["protocol_version"]);
        } else {
            $this->setHttpVersion(self::HTTP_VERSION_1_1);
        }

        if (array_key_exists("timeout", $param)) {
            $this->setTimeout($param["timeout"]);
        }

        /* make context */


        $this->_makeContext();
    }

    /**
     *  Make context parameters for stream_contect_create.
     */
    public function _makeContext() {


        $arrayContext = array("http" => array(
                "method" => $this->getMethod(),
                "header" => $this->_implodeHeader(),
                "follow_location" => $this->getFollowLocation(),
                "max_redirects" => $this->getMaxRedirect(),
                "protocol_version" => $this->getHttpVersion(),
                "timeout" => $this->getTimeOut()
                ));

        /* if method are POST/PUT , add content to stream_context */
        if (($this->getMethod() == $this->_methods[self::HTTP_METHOD_POST]) || ($this->getMethod() == $this->_methods[self::HTTP_METHOD_PUT])) {
            $arrayContext["http"]["content"] = http_build_query($this->_postData);
//            $arrayContext[$this->getScheme()]["content"] = implode("&\r\n", $postData);
        }

        /* if method is GET , add http_build_query of data to this->_url */

        if ($this->getMethod() == $this->_methods[self::HTTP_METHOD_GET]) {
            $this->_url .= "?" . http_build_query($this->_postData);
        }
//		echo '<pre>';
//		var_dump($arrayContext);
//		echo '</pre>';
        $this->_context = stream_context_create($arrayContext);
    }

    /**
     *
     * @return mixed Return body and headers of request
     */
    public function send() {
        if (!$this->_context) {
            $this->_makeContext();
        }
        $getContents = file_get_contents($this->getUrl(), null, $this->_context);
        $this->_response = $getContents;
        return $getContents;
    }

    /**
     *
     * @return mixed Return body and headers of request
     */
    public function getResponse() {
        return $this->_response;
    }

    /**
     *
     * @param String $url Url for request
     */
    public function setUrl($url) {
        if (is_null($url)) {
            return;
        }

        $this->_url = $url;
    }

    /**
     * Set a list of headers
     * @param Array $headers array of headers , format (header => value)
     */
    public function setHeaders($headers) {
        if (!is_array($headers)) {
            return;
        }

        foreach ($headers as $header => $value) {
            $this->setHeader($header, $value);
        }
    }

    /**
     * Set a single header
     * @param String $header Name of header (Eg. User-Agent, Content-type, etc)
     * @param String $value Value of Header
     */
    public function setHeader($header, $value) {
        if (is_null($header)) {
            return;
        }
        $this->_headers[$header] = $value;
    }

    /**
     * Get list of headers
     * @return Array 
     */
    public function getHeaders() {
        return $this->_headers;
    }

    /**
     * Setting the method for request
     * @param Int $method value of HTTP_METHOD_* constants
     */
    public function setMethod($method = self::HTTP_METHOD_GET) {

        if (is_numeric($method)) {
            if (array_key_exists($method, $this->_methods)) {
                $this->_method = $this->_methods[$method];
            } else {
                $this->_method = $this->_methods[self::HTTP_METHOD_GET];
            }
        } else {
            $this->_method = $method;
        }
    }

    /**
     * Return type of request
     * @return String
     */
    public function getMethod() {
        return $this->_method;
    }

    /**
     * Set a list of post data
     * @param Array Post data for request, format array(post1 => value1, etc)
     */
    public function setPostData($param) {
        if (is_array($param)) {
            foreach ($param as $post => $data) {
                $this->setPost($post, $data);
            }
        }
    }

    /**
     * Set a single post data
     * @param String $post Name of post (eg. username, password, city, etc)
     * @param String $data Value of post
     */
    public function setPost($post, $data) {
        if (is_null($post)) {
            return;
        }
        $this->_postData[$post] = $data;
    }

    /**
     * Return the value of a post $post
     * @param String $post Name of post data
     * @return String Value of $post
     */

    public function getPost($post) {
        if (is_null($post)) {
            return;
        }
        return $this->_postData[$post];
    }


    /**
     * Setting max number of redirect to follow in a request
     * @param Int $param Max number
     */
    public function setMaxRedirect($param) {
        if (is_null($param)) {
            return;
        }
        if (is_numeric($param)) {
            $this->_max_redirects = (int) $param;
        }
    }

    /**
     * Get the max redirect number
     * @return int
     */

    public function getMaxRedirect() {
        return $this->_max_redirects;
    }



    /**
     * Setting true/false the following location parameter of a stream context
     * @param bool $param true/false for following location (http 302)
     */
    public function setFollowLocation($param) {
        if (is_null($param)) {
            return;
        }
        if (is_bool($param)) {
            $this->_follow_location = $param;
        }
    }

    /**
     * Getting state of following_location parameter
     * @return bool
     */
    public function getFollowLocation() {
        return $this->_follow_location;
    }

    /**
     * Setting the http version to HTTP/1.0 or HTTP/1.1
     * @param Int $param Http Version value of HTTP_VERSION_* constants
     */
    public function setHttpVersion($param) {
        if (is_null($param)) {
            return;
        }
        if (array_key_exists($param, $this->_versions)) {
            $this->_version = $this->_versions[$param];
        }
    }

    /**
     * Get the HTTP Version of a request
     * @return String
     */
    public function getHttpVersion() {
        return $this->_version;
    }

    /**
     * Setting the timeout connection for request
     * @param Integer $param number of timeout connection
     */
    public function setTimeOut($param) {
        if (is_numeric($param)) {
            $this->_timeout = (int) $param;
        }
    }


    /**
     * Get the timeout connection for request
     * @return Integer
     */
    public function getTimeOut() {
        return $this->_timeout;
    }

    /**
     * Setting the user-agent header for request
     * @param String $value User-Agent value
     */
    public function setUserAgent($value) {
        if (is_null($value)) {
            return;
        }
        $this->_headers["User-Agent"] = $value;
    }

    /**
     * Get the User-Agent value for request
     * @return String
     */
    public function getUserAgent() {
        return $this->_headers["User-Agent"];
    }

    /**
     * Return url of request
     * @return String
     */
    public function getUrl() {
        return $this->_url;
    }

    /**
     * Return scheme of request (http/https)
     * @return String
     */
    public function getScheme() {
        return parse_url($this->_url, PHP_URL_SCHEME);
    }

    /**
     * Return host of request
     * @return String
     */
    public function getHost() {
        return parse_url($this->_url, PHP_URL_HOST);
    }

    /**
     * Return port of http request (default value 80)
     * @return Numeric
     */
    public function getPort() {
        if (!parse_url($this->_url, PHP_URL_PORT)) {
            return 80;
        }
        return parse_url($this->_url, PHP_URL_PORT);
    }

    /**
     * Return path of request
     * @return String
     */
    public function getPath() {
        if (!parse_url($this->_url, PHP_URL_PATH)) {
            return "/";
        }
        return parse_url($this->_url, PHP_URL_PATH);
    }

    /**
     * Make headers for stream_context_create
     */
    private function _implodeHeader() {
        $_headers = array();
        foreach ($this->_headers as $header => $value) {
            $_headers[] = $header . ": " . $value;
        }
        return implode("\r\n", $_headers);
    }

}
